package gnss

import (
	"bytes"
	"dgo/goutils/utils"
	"encoding/binary"
	"fmt"
	"math"
	"time"
)

const (
	SIG_TYPE_GLO_G1   byte = 1
	SIG_TYPE_GLO_G2   byte = 2
	SIG_TYPE_GAL_E1        = 1
	SIG_TYPE_GAL_E5A       = 2
	SIG_TYPE_GAL_E5B       = 3
	SIG_TYPE_GAL_E6        = 4
	SIG_TYPE_GAL_E5AB      = 5
	SIG_TYPE_BDS_B1I       = FREQ_BD_BAND_TYPE_B1I
	SIG_TYPE_BDS_B2I       = FREQ_BD_BAND_TYPE_B2I
	SIG_TYPE_BDS_B3I       = FREQ_BD_BAND_TYPE_B3I
	SIG_TYPE_BDS_B1C       = FREQ_BD_BAND_TYPE_B1C
	SIG_TYPE_BDS_B2A       = FREQ_BD_BAND_TYPE_B2A
	SIG_TYPE_BDS_B2B       = FREQ_BD_BAND_TYPE_B2B
	SIG_TYPE_BDS_B3C       = FREQ_BD_BAND_TYPE_B3C
	SIG_TYPE_BDS_B2AB      = FREQ_BD_BAND_TYPE_B2AB
	SIG_TYPE_QZS_L1   byte = 1
	SIG_TYPE_QZS_L2   byte = 2
	SIG_TYPE_QZS_L5   byte = 5
)

var (
	POLYCRC32 uint32 = 0xEDB88320 /* CRC32 polynomial */
)

/*
*
extern unsigned int crc32(const unsigned char *buff, int len)

	{
	    unsigned int crc=0;
	    int i,j;

	    trace(4,"crc32: len=%d\n",len);

	    for (i=0;i<len;i++) {
	        crc^=buff[i];
	        for (j=0;j<8;j++) {
	            if (crc&1) crc=(crc>>1)^POLYCRC32; else crc>>=1;
	        }
	    }
	    return crc;
	}
*/
func Crc32(buf []byte) uint32 {
	if len(buf) == 0 {
		return 0
	}

	r := uint32(0)

	if len(buf) > 1 {
		for i := 0; i < len(buf); i++ {
			r ^= uint32(buf[i])
			for j := 0; j < 8; j++ {
				if r&1 == 1 {
					r = (r >> 1) ^ POLYCRC32
				} else {
					r >>= 1
				}
			}
		}
	}
	return r
}

func U4FromBuf(buf []byte) uint32 {
	return binary.LittleEndian.Uint32(buf)
}

func U2FromBuf(buf []byte) uint16 {
	return binary.LittleEndian.Uint16(buf)
}

/*
**
 */
func GetWaveLength(naviSys byte, sigtype byte, satid byte) float64 {

	switch naviSys {
	case SYS_SBS, SYS_GPS:
		//case :
		if sigtype == FREQ_GPS_TYPE_L1 { // L1
			return LIGHT_SPEED / FREQ_GPS_L1
		} else if sigtype == FREQ_GPS_TYPE_L2 {
			return LIGHT_SPEED / FREQ_GPS_2
		} else if sigtype == FREQ_GPS_TYPE_L5 {
			return LIGHT_SPEED / FREQ_GPS_5
		}
	case SYS_BD: // 1:B1I, 2:B2I, 3:B3I, 4:B1C, 5:B2A, 6:B2B, 7:B3C, 8:ACEBOC
		if sigtype == FREQ_BD_BAND_TYPE_B1I {
			return LIGHT_SPEED / FREQ_BD_B1I
		} else if sigtype == FREQ_BD_BAND_TYPE_B2I {
			return LIGHT_SPEED / FREQ_BD_B2I
		} else if sigtype == FREQ_BD_BAND_TYPE_B3I {
			return LIGHT_SPEED / FREQ_BD_B3I
		} else if sigtype == FREQ_BD_BAND_TYPE_B1C {
			return LIGHT_SPEED / FREQ_BD_B1C
		} else if sigtype == FREQ_BD_BAND_TYPE_B2A {
			return LIGHT_SPEED / FREQ_BD_B2A
		} else if sigtype == FREQ_BD_BAND_TYPE_B2B {
			return LIGHT_SPEED / FREQ_BD_B2B
		} else if sigtype == FREQ_BD_BAND_TYPE_B3C {
			return LIGHT_SPEED / FREQ_BD_B3C
		} else if sigtype == FREQ_BD_BAND_TYPE_B2AB {
			return LIGHT_SPEED / FREQ_BD_B2AB
		}
	case SYS_GLO:
		if satid == 0 || int(satid-1) >= len(GLO_FRQ) {
			return 0
		}
		fn := GLO_FRQ[satid-1]
		if sigtype == SIG_TYPE_GLO_G1 {
			return LIGHT_SPEED / (FREQ1_GLO + DFRQ1_GLO*fn)
		} else if sigtype == SIG_TYPE_GLO_G2 {
			return LIGHT_SPEED / (FREQ2_GLO + DFRQ2_GLO*fn)
		}
	case SYS_GAL:
		//if sigv <= 1 {
		//	return 0.0
		//} else if sigv >= 2 && sigv <= 6 { //2..6   E1
		//	return LIGHT_SPEED / FREQ_E1
		//} else if sigv >= 8 && sigv <= 12 { //8..12 E6
		//	return LIGHT_SPEED / FREQ_E6
		//} else if sigv >= 14 && sigv <= 16 { //14..16 E5B
		//	return LIGHT_SPEED / FREQ_E5B
		//} else if sigv >= 18 && sigv <= 20 { //18..20 E5(A+B)
		//	return LIGHT_SPEED / FREQ_E5AB
		//} else if sigv >= 22 && sigv <= 24 { //22..24 E5A
		//	return LIGHT_SPEED / FREQ_E5A
		//}
		if sigtype == SIG_TYPE_GAL_E1 {
			return LIGHT_SPEED / FREQ_E1
		} else if sigtype == SIG_TYPE_GAL_E6 {
			return LIGHT_SPEED / FREQ_E6
		} else if sigtype == SIG_TYPE_GAL_E5B {
			return LIGHT_SPEED / FREQ_E5B
		} else if sigtype == SIG_TYPE_GAL_E5AB {
			return LIGHT_SPEED / FREQ_E5AB
		} else if sigtype == SIG_TYPE_GAL_E5A {
			return LIGHT_SPEED / FREQ_E5A
		}

	case SYS_QZS:
		if sigtype == SIG_TYPE_QZS_L1 { // L1
			return LIGHT_SPEED / FREQ_GPS_L1
		} else if sigtype == SIG_TYPE_QZS_L2 {
			return LIGHT_SPEED / FREQ_GPS_2
		} else if sigtype == SIG_TYPE_QZS_L5 {
			return LIGHT_SPEED / FREQ_GPS_5
		}
	}
	return 0
}

func RoundU(val float64) uint64 {
	return uint64(math.Floor(val + 0.5))
}

func Round(val float64) int64 {
	return int64(math.Floor(val + 0.5))
}

// UTC到GPS需要增加一个秒
func UTC2GPSTime(utc time.Time) time.Time {
	return utc.Add(time.Second * LEAP_SECONDS)
}

// GPS到UTC当前时间需要减去一个秒
func GPSTime2UTC(gps time.Time) time.Time {
	return gps.Add(-time.Second * LEAP_SECONDS)
}

// GPS时间和当前时间的延迟
// 当前UTC时间 - GPS时间 + 18
func GpsTimeDelaySecs(gps time.Time) float64 {
	return utils.SecondSub(gps, time.Now()) + GPS_FASTER_THAN_UTC
}

// / double time2gpst(gtime_t t, int *week)  (返回周内秒), week 为周数
// utc数据转成  GPS周和周内秒
func ToGPSTime(utctime time.Time) (week uint32, sow float64) {
	startTime := GetStartTime(SYS_GPS)
	now := utctime
	if now.IsZero() {
		now = time.Now()
	}
	now = now.UTC()

	week = uint32(math.Floor(float64(now.Sub(startTime).Hours()) / float64(24) / float64(7)))
	startOfW := startTime.Add(A_WEEK * time.Duration(week))
	return week, now.Sub(startOfW).Seconds()
}

func DecodeGpsTime(t0 time.Time) (week uint32, sow float64) {
	startTime := GetStartTime(SYS_GPS)
	now := t0.UTC()
	week = uint32(math.Floor(float64(now.Sub(startTime).Hours()) / float64(24) / float64(7)))
	startOfW := startTime.Add(A_WEEK * time.Duration(week))
	return week, now.Sub(startOfW).Seconds()
}

func SetNowGPST(t time.Time) {
	GPST_SPAN = time.Now().Sub(t).Seconds()
}

func GetNowGPST() time.Time {
	if GPST_SPAN == 0 { // 没进行设定
		return time.Now().Add(time.Duration(GPS_FASTER_THAN_UTC*1000) * time.Millisecond)
	}
	return time.Now().Add(-(time.Duration(GPST_SPAN*1000) * time.Millisecond))
}

/*
*

	类似RTK的GPS延迟
	当前GPS时间-数据gps时间, 计算数据的延迟情况
*/
func RTKGPSDelaySecs(gpstime time.Time) float64 {
	if gpstime.IsZero() {
		return 9999
	}
	return GetNowGPST().Sub(gpstime).Seconds()
}

func RinexFormatFloatEx(fv float64) string {
	str := ""
	if fv == 0 {
		str += string(bytes.Repeat([]byte(" "), 13))
	} else {
		v := fmt.Sprintf("%.3f", fv)
		l := 13 - len(v)
		if l > 0 {
			str += string(bytes.Repeat([]byte(" "), l))
		}
		str += v
	}
	return str
}

func RinexFormatFloat(fv float64) string {
	str := ""
	if fv == 0 {
		str += string(bytes.Repeat([]byte(" "), 14))
	} else {
		v := fmt.Sprintf("%.3f", fv)
		l := 13 - len(v)
		if l > 0 {
			str += string(bytes.Repeat([]byte(" "), l))
		}
		str += v + " "
	}
	return str
}

/*
转成时间
*/
func WeekSOWToTime(week uint32, sow float64) time.Time {
	startTime := GetStartTime(SYS_GPS)
	//rval := startTime.Add(A_WEEK * time.Duration(week)).Add(time.Duration(sow * 1000 * 1000) * time.Microsecond);
	ms := time.Duration((sow * 1000) + 0.5) // 避免0.9999999的问题
	rval := startTime.Add(A_WEEK * time.Duration(week))
	rval = rval.Add(ms * time.Millisecond)
	return rval
}

func Toc2Time(toc float64, navsys byte, refUtc time.Time) time.Time {
	if refUtc.IsZero() {
		refUtc = time.Now().UTC()
	}
	startTime := GetStartTime(navsys)
	week := uint32(float64(refUtc.Sub(startTime).Hours()) / float64(24) / float64(7))
	rval := startTime.Add(A_WEEK * time.Duration(week)).Add(time.Duration(toc*1000*1000) * time.Microsecond)
	return rval
}

func BDToUtcTime(week uint32, sow float64) time.Time {
	startTime := GetStartTime(SYS_BD)
	rval := startTime.Add(A_WEEK * time.Duration(week)).Add(time.Duration(sow*1000*1000) * time.Microsecond)
	return rval
}

/***
 * rtklib time2gpst
 * return
 *   tow:   time of week in gps time (s)
 *   weekn: 周计数
 *
 */
func Time2GpsT(t time.Time) (weekn int, tow float64) {
	startT := GPS_START_TIME
	sec := t.Sub(startT).Seconds()
	weekn = int(sec / float64(86400*7))
	tow = sec - float64(weekn*86400*7)
	return
}

/*
*
 */
func Time2MJD(t time.Time) (mjd int, sod int) {
	/**
	  	    time=epoch2time(ep);
	        time2=utc2gpst(timeadd(time,0.0));
	        sow=time2gpst(time2,&week);
	        DLY.mjd=*mjd=(int)(sow/86400.0)+week*7+44244;
	        DLY.sod=*sod=fmod(sow,86400.0);
	*/
	t1 := UTC2GPSTime(t)
	week, sow := Time2GpsT(t1)
	mjd = int(sow/86400) + week*7 + 44244
	sod = int(sow) % 86400
	return
}

func AdjGpsWeek(weekn int, nowT time.Time) (afterWeek int) {
	if nowT.IsZero() {
		nowT = time.Now()
	}
	wn, _ := Time2GpsT(nowT)
	//if wn <1560 {   // 2009/12/1 if time is earlier than 2009/12/1
	//	wn = 1560
	//}
	r1 := weekn + (wn-weekn+512)/1024*1024
	return r1

	//int w;
	//(void)time2gpst(utc2gpst(timeget()),&w);
	//if (w<1560) w=1560; /* use 2009/12/1 if time is earlier than 2009/12/1 */
	//return week+(w-week+512)/1024*1024;
}

/**
 * gpst2time
 */
func Gpst2Time(weekn int, sec float64) time.Time {
	r1 := GPS_START_TIME.Add(time.Duration(weekn) * 86400 * 7 * time.Second).Add(time.Duration(sec) * time.Second)
	return r1
}

/**
 * 解码GAL时间周问题
 */
func AdjustWeekTime(toc float64, week uint32, navsys byte, refUtc time.Time) (afterWeek uint32, rvaltime time.Time) {
	if navsys == SYS_GAL {
		afterWeek = uint32(AdjGpsWeek(int(week)%1024, refUtc))
		rvaltime = Gpst2Time(int(afterWeek), toc)
		return
	} else {
		startTime := GetStartTime(navsys)
		var now = time.Now().UTC()
		if !refUtc.IsZero() {
			now = refUtc
		}
		// 参考时间的周计数
		wN := float64(now.Sub(startTime).Hours()) / float64(24) / float64(7)
		cycleW := GetCycleWeek(navsys)

		cyleTimes := uint32(wN / float64(cycleW))
		afterWeek = cyleTimes*uint32(cycleW) + week

		rvaltime = startTime.Add(A_WEEK * time.Duration(afterWeek))
		// rvaltime = rvaltime.Add(time.Duration(toc*1000*1000) * time.Microsecond)
		rvaltime = rvaltime.Add(time.Duration(toc * float64(time.Second)))
		return
	}
}

func GetNavSysName(isys byte) string {
	return NAV_SYS_NAME[isys]
}

func GetNavSysCode(isys byte) string {
	return GNSS_SYS_RINEX_CHAR[isys]
}

/*
*

	code char
*/
func GetNaviSysFromNaviCode(code byte) byte {
	switch code {
	case 'C':
		return SYS_BD
	case 'G':
		return SYS_GPS
	case 'R':
		return SYS_GLO
	case 'E':
		return SYS_GAL
	case 'J':
		return SYS_QZS
	}
	return SYS_UNKOWN
}

func ExtractSatInfoFromPrn(prn string) (navisys byte, satid byte) {
	if len(prn) < 3 {
		return
	}

	navisys = GetNaviSysFromNaviCode(prn[0])
	if navisys == SYS_UNKOWN {
		return
	}

	satid = byte(utils.StrToIntDef(prn[1:], 0))
	return
}
